package spring.cloud.tasks.executor.listener.config;

import com.google.common.base.Strings;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import spring.cloud.tasks.executor.common.Service;
import spring.cloud.tasks.executor.exception.ShardingItemParametersException;
import spring.cloud.tasks.executor.node.Node;
import spring.cloud.tasks.executor.task.TaskExecutor;
import spring.cloud.tasks.executor.task_type.TaskType;
import spring.cloud.tasks.executor.task_type.TaskTypeManager;
import spring.cloud.tasks.executor.utils.ThreadFactory;
import spring.cloud.tasks.executor.utils.log.LogEvents;
import spring.cloud.tasks.executor.utils.log.LogUtils;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 弹性化分布式作业配置服务.
 */
@Slf4j
@Setter
@Getter
public class TaskConfigurationService extends Service {


    /**
     * 获取分片序列号和个性化参数对照表.<br>
     * 如果是本地模式的作业，则获取到[-1=xx]
     *
     * @return 分片序列号和个性化参数对照表
     */
    private static final String DOUBLE_QUOTE = "\"";
    // 参考http://stackoverflow.com/questions/17963969/java-regex-pattern-split-commna
    private static final String PATTERN = ",(?=(([^\"]*\"){2})*[^\"]*$)";
    private ExecutorService executorService;

    public TaskConfigurationService(TaskExecutor taskExecutor) {
        super(taskExecutor);
    }

    private static boolean checkIsInPausePeriodDateOrTime(int h, int m, String splitChar, String pausePeriodDateOrTime) {
        String[] periods = pausePeriodDateOrTime.split(",");

        boolean result = false;
        for (String period : periods) {
            String[] tmp = period.trim().split("-");
            if (tmp != null && tmp.length == 2) {
                String left = tmp[0].trim();
                String right = tmp[1].trim();
                String[] hmLeft = left.split(splitChar);
                String[] hmRight = right.split(splitChar);
                if (hmLeft != null && hmLeft.length == 2 && hmRight != null && hmRight.length == 2) {
                    try {
                        int hLeft = Integer.parseInt(hmLeft[0]);
                        int mLeft = Integer.parseInt(hmLeft[1]);
                        int hRight = Integer.parseInt(hmRight[0]);
                        int mRight = Integer.parseInt(hmRight[1]);
                        result = (h > hLeft || h == hLeft && m >= mLeft) // NOSONAR
                                && (h < hRight || h == hRight && m <= mRight); // NOSONAR
                        if (result) {
                            break;
                        }
                    } catch (NumberFormatException e) {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
        return result;
    }

    @Override
    public void start() {
        super.start();
        executorService = Executors.newSingleThreadExecutor(new ThreadFactory(executorId + "-" + taskId + "-enabledChanged", false));
    }

    @Override
    public void stop() {
        super.stop();
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    public void notifyJobEnabledOrNot() {
        if (isEnabled()) {
            notifyTaskEnabledIfNecessary();
        } else {
            notifyTaskDisabled();
        }
    }

    /**
     * 获取是否开启作业.
     *
     * @return 作业是否开启
     */
    public boolean isEnabled() {
        return taskConfiguration.isEnabled();
    }

    /**
     * 如果是本地模式，并且配置了优先Executor，并且当前Executor不属于优先Executor，则不需要通知
     */
    public void notifyTaskEnabledIfNecessary() {

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    taskExecutor.getTaskExecuteEngine().notifyTaskEnabled();
                } catch (Throwable t) {
                    LogUtils.error(log, LogEvents.ExecutorEvent.COMMON, t.getMessage(), t);
                }
            }
        });
    }

    public void notifyTaskDisabled() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    taskExecutor.getTaskExecuteEngine().notifyTaskDisabled();
                } catch (Throwable t) {
                    LogUtils.error(log, LogEvents.ExecutorEvent.COMMON, t.getMessage(), t);
                }
            }
        });
    }

    public TaskType getTaskType() {
        return TaskTypeManager.get(taskConfiguration.getTaskType());
    }

    /**
     * 获取作业启动时间的cron表达式.
     *
     * @return 作业启动时间的cron表达式
     */
    public String getCronExpression() {
        return taskConfiguration.getCronExpression();
    }

    /**
     * 获取作业分片总数.
     *
     * @return 作业分片总数
     */
    public int getTaskItemTotalCount() {
        return taskConfiguration.getTaskItemTotalCount();
    }

    public Map<Integer, String> getTaskItemIndexToTaskItemAndParameterMap() {
        Map<Integer, String> taskItemIndexToTaskItemAndParameterMap = new HashMap<>();
        String taskItemParameters = taskConfiguration.getTaskItemParameters();
        if (Strings.isNullOrEmpty(taskItemParameters)) {
            return taskItemIndexToTaskItemAndParameterMap;
        }
        // 解释命令行参数
        String[] taskItemParameterStrings = taskItemParameters.split(PATTERN);
        Map<String, String> map = new HashMap<>(taskItemParameterStrings.length);
        for (String taskItemParameter : taskItemParameterStrings) {
            String taskItemIndex = "";
            String taskItemAndParameter = "";
            int index = taskItemParameter.indexOf('=');
            if (index > -1) {
                taskItemIndex = taskItemParameter.substring(0, index).trim();
                taskItemAndParameter = taskItemParameter.substring(index + 1).trim();
                // 去掉前后的双引号"
                if (taskItemAndParameter.startsWith(DOUBLE_QUOTE)) {
                    taskItemAndParameter = taskItemAndParameter.substring(1);
                }
                if (taskItemAndParameter.endsWith(DOUBLE_QUOTE)) {
                    taskItemAndParameter = taskItemAndParameter.substring(0, taskItemAndParameter.length() - 1);
                }
            } else {
                throw new ShardingItemParametersException("Sharding item parameters '%s' format error", taskItemParameters);
            }
            map.put(taskItemIndex, taskItemAndParameter);
        }
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            String taskItemIndex = next.getKey();
            String taskItemAndParameter = next.getValue();
            try {
                taskItemIndexToTaskItemAndParameterMap.put(Integer.valueOf(taskItemIndex), taskItemAndParameter);
            } catch (final NumberFormatException ex) {
                LogUtils.warn(log, LogEvents.ExecutorEvent.COMMON, "Sharding item key '%s' is invalid, it should be an integer, key '%s' will be dropped", taskItemIndex, taskItemIndex, ex);
            }
        }
        return taskItemIndexToTaskItemAndParameterMap;
    }

    /**
     * 获取作业自定义参数.
     *
     * @return 作业自定义参数
     */
    public String getTaskParameter() {
        return taskConfiguration.getTaskParameter();
    }

    /**
     * 获取自定义上下文
     *
     * @return 获取自定义上下文
     */
    public Map<String, String> getCustomContext() {
        String jobNodeData = this.getTaskNodeStorage().getTaskNodeData(ConfigurationNode.CUSTOM_CONTEXT);
        return null;
    }

    /**
     * 本机当前时间是否在作业暂停时间段范围内。
     * <p>
     * 特别的，无论pausePeriodDate，还是pausePeriodTime，如果解析发生异常，则忽略该节点，视为没有配置该日期或时分段。
     *
     * @return 本机当前时间是否在作业暂停时间段范围内.
     */
    public boolean isInPausePeriod() {
        return isInPausePeriod(new Date());
    }

    /**
     * 该时间是否在作业暂停时间段范围内。
     * <p>
     * 特别的，无论pausePeriodDate，还是pausePeriodTime，如果解析发生异常，则忽略该节点，视为没有配置该日期或时分段。
     *
     * @param date 时间，本机时区的时间
     * @return 该时间是否在作业暂停时间段范围内。
     */
    public boolean isInPausePeriod(Date date) {
        Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
        calendar.setTime(date);
        int month = calendar.get(Calendar.MONTH) + 1; // Calendar.MONTH begin from 0.
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        //
        boolean dateIn = false;
        String pausePeriodDate = taskConfiguration.getPauseDatePeriod();
        boolean pausePeriodDateIsEmpty = (pausePeriodDate == null || pausePeriodDate.trim().isEmpty());
        if (!pausePeriodDateIsEmpty) {
            dateIn = checkIsInPausePeriodDateOrTime(month, day, "/", pausePeriodDate);
        }
        //
        boolean timeIn = false;
        String pausePeriodTime = taskConfiguration.getPauseTimePeriod();
        boolean pausePeriodTimeIsEmpty = (pausePeriodTime == null || pausePeriodTime.trim().isEmpty());
        if (!pausePeriodTimeIsEmpty) {
            timeIn = checkIsInPausePeriodDateOrTime(hour, minute, ":", pausePeriodTime);
        }
        //
        if (pausePeriodDateIsEmpty) {
            if (pausePeriodTimeIsEmpty) {
                return false;
            } else {
                return timeIn;
            }
        } else {
            if (pausePeriodTimeIsEmpty) {
                return dateIn;
            } else {
                return dateIn && timeIn;
            }
        }
    }

    /**
     * 获取是否开启失效转移.
     *
     * @return 是否开启失效转移
     */
    public boolean isFailover() {
        return taskConfiguration.isFailover();
    }


    /**
     * 获取是否显示正常日志
     *
     * @return 是否显示正常日志
     */
    public boolean showNormalLog() {
        return taskConfiguration.isShowNormalLog();
    }

    /**
     * 获取统计作业处理数据数量的间隔时间.
     *
     * @return 统计作业处理数据数量的间隔时间
     */
    public int getProcessCountIntervalSeconds() {
        return taskConfiguration.getProcessCountIntervalSeconds();
    }

    /**
     * 获取超时时间
     *
     * @return 超时时间
     */
    public int getTimeoutSeconds() {
        return taskConfiguration.getTimeoutSeconds();
    }
    //

    public List<String> getPreferExecutorIdList() {
        List<String> $preferExecutorIdList$ = new ArrayList<String>();
        String preferExecutorIdList = taskConfiguration.getPreferExecutorIdList();
        if (preferExecutorIdList == null) {
            return $preferExecutorIdList$;
        }
        String[] preferExecutorIds = preferExecutorIdList.split(",");
        List<String> executorIdList = this.getExecutorIdList();
        for (String executorId : executorIdList) {
            for (String preferExecutorId : preferExecutorIds) {
                if (preferExecutorId.equals(executorId)) {
                    if (!preferExecutorIdList.contains(executorId)) {
                        $preferExecutorIdList$.add(executorId);
                        break;
                    }
                }
            }
        }
        return $preferExecutorIdList$;
    }

    private List<String> getExecutorIdList() {
        List<String> executorIdList = new ArrayList<>();
        if (zookeeper.checkExists(Node.getExecutorsNodePath())) {
            List<String> executors = zookeeper.getChildrenList(Node.getExecutorsNodePath());
            if (executors != null) {
                executorIdList.addAll(executors);
            }
        }
        return executorIdList;
    }

    public boolean isUseDispreferList() {
        return taskConfiguration.isUseDisExecutorIdList();
    }


}
